2.5 Vehicle Miles Traveled

Building off the Origin-Destination Commute Analysis from the previous section, we isolated every unique pair of SJC or Stockton home block groups and work block groups and used the Open Source Routing Machine to compute a distance (in miles) and duration (and hours) for a one-way trip. This analysis, combined with some estimates of mode choice (single-occupancy vehicle vs. carpool vs. other), we created estimates of vehicle miles traveled for a certain number of jobs and were able to see how see how SJC and Stockton residents have changed their driving behavior over time, and how that behavior has compared to neighboring counties. Finally, we used VMT to estimate the GHG footprint of commuting for Stockton residents.

Our analysis in this section focuses on destinations with average commute times of less than 3 hours, which eliminates the unrealistic commutes while preserving more than 90% of reported commutes. We suspect that many of the other commutes with purported destinations like Los Angeles County are erroneously reported, and the actual workplace location is unavailable to us. Note, however, that it could be true that some workers commute to southern California, though not every weekday, and it also could be true to some workers commute by flight, which presents its own distinct and potentially significant GHG analysis which is outside the scope of this report.

2.5.1 County-level Analysis

The following table shows commute data for SJC residents from 2011 to 2017, including estimates of the number of total jobs held by residents, the number of those jobs that are car-dependent, and the number of those jobs that are carpooled (according to ACS). The routing analysis provides distances between each origin and destination, which are totaled up to person miles traveled. The conversion to vehicle miles traveled is based on the following assumptions:

  • For each origin-destination block group pair, we used ACS 1-yr 2017 data on commute mode by travel time for the origin block group to estimate a share of travelers commuting by single occupancy vehicle and by carpooling, and applied this factor to the count of jobs from LODES to estimate a total number of vehicle trips and vehicle mildes traveled (VMT).
  • For carpool trips, we counted 2-person carpools as 1 vehicle for every 2 jobs, and 3-or-more-person carpools as 1 vehicle for every 3 jobs.
# for(year in 2011:2017){
#   for(county in c("013")){
# 
#     print(paste0(county,"-",year))
# 
#     ca_lodes <-
#       grab_lodes(
#         state = "ca",
#         year = year,
#         lodes_type = "od",
#         job_type = "JT01", #Primary Jobs
#         segment = "S000",
#         state_part = "main",
#         agg_geo = "tract"
#       )
#     
#     save(ca_lodes, file = paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/ca_lodes_",year,"_tract.Rdata"))
# 
#     county_tracts <-
#       ca_tracts %>%
#       filter(COUNTYFP == county)
# 
#     county_lodes_h <-
#       ca_lodes[which(ca_lodes$h_tract %in% county_tracts$GEOID),]
# 
#     county_lodes_h_origin_centroids <-
#       st_centroid(ca_tracts[which(ca_tracts$GEOID %in% county_lodes_h$h_tract),])
# 
#     county_lodes_h_dest_centroids <-
#       st_centroid(ca_tracts[which(ca_tracts$GEOID %in% county_lodes_h$w_tract),])
# 
#     route <-
#       1:nrow(county_lodes_h) %>%
#       map_dfr(function(row){
#         print(row)
#         route <- osrmRoute(
#           src = county_lodes_h_origin_centroids[which(county_lodes_h_origin_centroids$GEOID %in% county_lodes_h[row,"h_tract"]),],
#           dst = county_lodes_h_dest_centroids[which(county_lodes_h_dest_centroids$GEOID %in% county_lodes_h[row,"w_tract"]),],
#           overview = FALSE
#         ) %>%
#           as.list() %>%
#           as.data.frame()
#         if(is_empty(route)){
#           return(
#             data.frame(
#               duration = NA,
#               distance = NA
#             )
#           )
#         } else {return(route)}
#       })
# 
#     county_lodes_h_route <-
#       county_lodes_h %>%
#       cbind(route)
# 
#     county_lodes_h_filter <-
#       county_lodes_h_route %>%
#       filter(duration < 180)
# 
#     save(county_lodes_h_route,county_lodes_h_filter, file = paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_lodes_",county,"_",year,"_tract.Rdata"))
#   }
# }

# county_commute_vmt_tractmode <- NULL
# 
# for(year in 2011:2017){
#   for(county in c("013")){
#     print(paste0(year,"-",county))
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_lodes_",county,"_",year,"_tract.Rdata"))
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/acs5_vars_",year,".Rdata"))
# 
#     travel_time_mode <-
#       getCensus(
#         name = "acs/acs5",
#         vintage = year,
#         region = "tract:*",
#         regionin = paste0("state:06+county:",county),
#         vars = "group(B08134)"
#       ) %>%
#       mutate(tract = paste0(state,county,tract)) %>%
#       select_if(!names(.) %in% c("GEO_ID","state","county","NAME")) %>%
#       dplyr::select(-c(contains("EA"),contains("MA"),contains("M"))) %>%
#       gather(
#         key = "variable",
#         value = "estimate",
#         -tract
#       ) %>%
#       mutate(
#         label = acs_vars$label[match(variable,acs_vars$name)],
#         time = # This extracts only the time information from our label.
#           substr(
#             label,
#             lapply(
#               label,
#               function(x){
#                 ifelse(
#                   length(unlist(gregexpr('!!',x)))<3,
#                   NA,
#                   max(unlist(gregexpr('!!',x)))+2
#                 )
#               }
#             ),
#             nchar(label)
#           ),
#         mode = # This extracts only the mode information from our label. It doesn't fully deal with double-counting, so some are further removed later on in a filter.
#           substr(
#             label,
#             lapply(
#               label,
#               function(x){
#                 ifelse(
#                   length(unlist(gregexpr('!!',x)))<3,
#                   NA,
#                   unlist(gregexpr('!!',x))[length(unlist(gregexpr('!!',x)))-1]+2
#                 )
#               }
#             ),
#             lapply(
#               label,
#               function(x){
#                 max(unlist(gregexpr('!!',x)))-1
#               }
#             )
#           )
#       ) %>%
#       filter(!is.na(time)) %>% # This removes the grand total rows that are usually at the top of the ACS data.
#       filter(!mode %in% c("Car, truck, or van","Car truck or van","Carpooled","Public transportation (excluding taxicab)")) %>% # This removes double-counted subtotals.
#       dplyr::select(-variable,-label)
# 
#     travel_time_mode_summary <-
#       travel_time_mode %>%
#       group_by(tract,time) %>%
#       summarize(
#         jobs = sum(estimate),
#         jobs_drovealone = sum(estimate[which(mode == "Drove alone")]),
#         jobs_carpool2 = sum(estimate[which(mode == "In 2-person carpool")]),
#         jobs_carpool3 = sum(estimate[which(mode == "In 3-or-more-person carpool")]),
#       ) %>%
#       mutate(
#         vehicles_drovealone = jobs_drovealone,
#         vehicles_carpool2 = jobs_carpool2/2,
#         vehicles_carpool3 = jobs_carpool3/3,
#         jobs_car = jobs_drovealone+jobs_carpool2+jobs_carpool3,
#         jobs_carpool = jobs_carpool2+jobs_carpool3,
#         vehicles = vehicles_drovealone,vehicles_carpool2,vehicles_carpool3,
#         perc_jobs_car = jobs_car/jobs,
#         perc_jobs_carpool = jobs_carpool/jobs,
#         perc_vehicle = vehicles/jobs
#       )
# 
#   county_lodes_h_mode <-
#     county_lodes_h_filter %>%
#     transmute(
#       residence = h_tract,
#       jobs = S000,
#       duration = duration,
#       person_miles = S000*as.numeric(distance)/1.60934,
#       person_hours = S000*as.numeric(duration)/60
#     ) %>%
#     mutate(
#       tier =
#         case_when(
#           duration < 10 ~ "Less than 10 minutes",
#           duration < 15 ~ "10 to 14 minutes",
#           duration < 20 ~ "15 to 19 minutes",
#           duration < 25 ~ "20 to 24 minutes",
#           duration < 30 ~ "25 to 29 minutes",
#           duration < 35 ~ "30 to 34 minutes",
#           duration < 45 ~ "35 to 44 minutes",
#           duration < 60 ~ "45 to 59 minutes",
#           TRUE ~ "60 or more minutes"
#         )
#     ) %>%
#     left_join(
#       travel_time_mode_summary %>%
#         dplyr::select(
#           tract,
#           time,
#           perc_jobs_car,
#           perc_jobs_carpool,
#           perc_vehicle
#         ),
#       by = c("residence" = "tract", "tier" = "time")
#     ) %>%
#     mutate(
#       jobs_car = jobs*perc_jobs_car,
#       jobs_carpool = jobs*perc_jobs_carpool,
#       vehicles = jobs*perc_vehicle,
#       vmt = person_miles*perc_vehicle
#     )
# 
#     county_commute_vmt_tractmode <-
#       rbind(county_commute_vmt_tractmode,
#         data.frame(
#           Year = year,
#           County = county,
#           person_miles = sum(county_lodes_h_mode$person_miles, na.rm=T),
#           jobs = sum(county_lodes_h_mode$jobs, na.rm=T),
#           jobs_car = sum(county_lodes_h_mode$jobs_car, na.rm=T),
#           jobs_carpool = sum(county_lodes_h_mode$jobs_carpool, na.rm=T),
#           vehicles = sum(county_lodes_h_mode$vehicles, na.rm = T),
#           vmt = sum(county_lodes_h_mode$vmt, na.rm=T)
#         )
#       )
# 
#     save(county_commute_vmt_tractmode,file = "C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_commute_vmt_tractmode.Rdata")
#   }
# }
#
# county_commute_vmt_countymode <- NULL
# 
# for(year in 2011:2017){
#   for(county in c("013")){
# 
#     print(paste0(year,"-",county))
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_lodes_",county,"_",year,"_tract.Rdata"))
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/acs1_vars_",year,".Rdata"))
# 
#     travel_time_mode <-
#     getCensus(
#       name = "acs/acs1",
#       vintage = year,
#       region = paste0("county:",county),
#       regionin = "state:06",
#       vars = "group(B08134)"
#     ) %>%
#     select_if(!names(.) %in% c("GEO_ID","state","NAME")) %>%
#     dplyr::select(-c(contains("EA"),contains("MA"),contains("M"))) %>%
#     gather(
#       key = "variable",
#       value = "estimate",
#       -county
#     ) %>%
#     mutate(
#       label = acs_vars$label[match(variable,acs_vars$name)],
#       time = # This extracts only the time information from our label.
#         substr(
#           label,
#           lapply(
#             label,
#             function(x){
#               ifelse(
#                 length(unlist(gregexpr('!!',x)))<3,
#                 NA,
#                 max(unlist(gregexpr('!!',x)))+2
#               )
#             }
#           ),
#           nchar(label)
#         ),
#       mode = # This extracts only the mode information from our label. It doesn't fully deal with double-counting, so some are further removed later on in a filter.
#         substr(
#           label,
#           lapply(
#             label,
#             function(x){
#               ifelse(
#                 length(unlist(gregexpr('!!',x)))<3,
#                 NA,
#                 unlist(gregexpr('!!',x))[length(unlist(gregexpr('!!',x)))-1]+2
#               )
#             }
#           ),
#           lapply(
#             label,
#             function(x){
#               max(unlist(gregexpr('!!',x)))-1
#             }
#           )
#         )
#     ) %>%
#     filter(!is.na(time)) %>% # This removes the grand total rows that are usually at the top of the ACS data.
#     filter(!mode %in% c("Car, truck, or van","Car truck or van","Carpooled","Public transportation (excluding taxicab)")) %>% # This removes double-counted subtotals.
#     dplyr::select(-variable,-label)
# 
#     travel_time_mode_summary <-
#       travel_time_mode %>%
#       group_by(time) %>%
#       summarize(
#         jobs = sum(estimate),
#         jobs_drovealone = sum(estimate[which(mode == "Drove alone")]),
#         jobs_carpool2 = sum(estimate[which(mode == "In 2-person carpool")]),
#         jobs_carpool3 = sum(estimate[which(mode == "In 3-or-more-person carpool")]),
#       ) %>%
#       mutate(
#         vehicles_drovealone = jobs_drovealone,
#         vehicles_carpool2 = jobs_carpool2/2,
#         vehicles_carpool3 = jobs_carpool3/3,
#         jobs_car = jobs_drovealone+jobs_carpool2+jobs_carpool3,
#         jobs_carpool = jobs_carpool2+jobs_carpool3,
#         vehicles = vehicles_drovealone,vehicles_carpool2,vehicles_carpool3,
#         perc_jobs_car = jobs_car/jobs,
#         perc_jobs_carpool = jobs_carpool/jobs,
#         perc_vehicle = vehicles/jobs
#       )
# 
#   county_lodes_h_mode <-
#     county_lodes_h_filter %>%
#     transmute(
#       jobs = S000,
#       duration = duration,
#       person_miles = S000*as.numeric(distance)/1.60934,
#       person_hours = S000*as.numeric(duration)/60
#     ) %>%
#     mutate(
#       tier =
#         case_when(
#           duration < 10 ~ "Less than 10 minutes",
#           duration < 15 ~ "10 to 14 minutes",
#           duration < 20 ~ "15 to 19 minutes",
#           duration < 25 ~ "20 to 24 minutes",
#           duration < 30 ~ "25 to 29 minutes",
#           duration < 35 ~ "30 to 34 minutes",
#           duration < 45 ~ "35 to 44 minutes",
#           duration < 60 ~ "45 to 59 minutes",
#           TRUE ~ "60 or more minutes"
#         )
#     ) %>%
#     left_join(
#       travel_time_mode_summary %>%
#         dplyr::select(
#           time,
#           perc_jobs_car,
#           perc_jobs_carpool,
#           perc_vehicle
#         ),
#       by = c("tier" = "time")
#     ) %>%
#     mutate(
#       jobs_car = jobs*perc_jobs_car,
#       jobs_carpool = jobs*perc_jobs_carpool,
#       vehicles = jobs*perc_vehicle,
#       vmt = person_miles*perc_vehicle
#     )
# 
#     county_commute_vmt_countymode <-
#       rbind(county_commute_vmt_countymode,
#         data.frame(
#           Year = year,
#           County = county,
#           person_miles = sum(county_lodes_h_mode$person_miles, na.rm=T),
#           jobs = sum(county_lodes_h_mode$jobs, na.rm=T),
#           jobs_car = sum(county_lodes_h_mode$jobs_car, na.rm=T),
#           jobs_carpool = sum(county_lodes_h_mode$jobs_carpool, na.rm=T),
#           vehicles = sum(county_lodes_h_mode$vehicles, na.rm = T),
#           vmt = sum(county_lodes_h_mode$vmt, na.rm=T)
#         )
#       )
#   }
# }
# 
# save(county_commute_vmt_countymode, file = "C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_commute_vmt_countymode.Rdata")
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_commute_vmt_countymode.Rdata")
# 
# county_commute_vmt_countymode %>% 
#   filter(jobs_car != 0) %>% 
#   ggplot(
#     aes(
#       x = Year,
#       y = vmt/jobs,
#       colour = County
#     )
#   ) +
#   geom_line()
# 
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_commute_vmt_tractmode.Rdata")
# 
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_commute_vmt_countymode.Rdata")
# 
# county_commute_vmt_compare <-
#   rbind(
#     county_commute_vmt_tractmode %>% 
#       mutate(type = "tract"),
#     county_commute_vmt_countymode %>% 
#       mutate(type = "county")
#   )
# 
# county_commute_vmt_compare %>% 
#   filter(jobs_car != 0) %>% 
#   ggplot(
#     aes(
#       x = Year,
#       y = vmt/jobs,
#       linetype = type,
#       colour = County
#     )
#   ) +
#   geom_line()

load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_commute_vmt_tractmode.Rdata")

county_commute_vmt_tractmode_table <-
  county_commute_vmt_tractmode %>% 
  filter(County == "077") %>%
  left_join(county_neighbors, by = c("County" = "COUNTYFP")) %>% 
  transmute(
    Year = Year,
    `Jobs held by SJC residents` = prettyNum(round(jobs,-3),big.mark=","),
    `Car-dependent jobs` = prettyNum(round(jobs_car,-3),big.mark=","),
    `Carpooled jobs` = prettyNum(round(jobs_carpool,-3),big.mark=","),
    `Vehicles driven by SJC residents` = prettyNum(round(vehicles,-3),big.mark=","),
    `Person miles traveled` = prettyNum(round(person_miles,-4),big.mark=","),
    `Vehicle miles traveled` = prettyNum(round(vmt,-4),big.mark=",")
  )

kable(
  county_commute_vmt_tractmode_table,
  booktabs = TRUE,
  caption = 'Commute data for SJC residents, 2011 to 2017. Data from LODES.'
) %>% 
  kable_styling() %>% 
  scroll_box(width = "100%")
Table 16: Commute data for SJC residents, 2011 to 2017. Data from LODES.
Year Jobs held by SJC residents Car-dependent jobs Carpooled jobs Vehicles driven by SJC residents Person miles traveled Vehicle miles traveled
2011 202,000 187,000 32,000 156,000 6,460,000 4,540,000
2012 205,000 192,000 33,000 159,000 6,610,000 4,690,000
2013 213,000 200,000 33,000 167,000 6,970,000 5,080,000
2014 221,000 207,000 35,000 172,000 7,260,000 5,240,000
2015 229,000 215,000 38,000 176,000 7,380,000 5,170,000
2016 238,000 223,000 39,000 184,000 7,810,000 5,520,000
2017 248,000 232,000 39,000 193,000 8,280,000 5,930,000

\(~\)

The following figure shows one-way commute VMT/job for SJC and its neighboring counties from 2011 to 2017. Note that this VMT/job ratio divides VMTs equally among all car-dependent jobs, leaving out non-car-dependent jobs (never more than 10% of jobs, as can be seen in the previous table).

SJC is at the top of the figure, 25 miles/job in 2017, vying with Stanislaus County to have the highest average commute distance per car-dependent worker.

county_commute_vmt_tractmode %>% 
  left_join(county_neighbors, by = c("County" = "COUNTYFP")) %>% 
  ggplot(
    aes(
      x = Year,
      y = vmt/jobs_car,
      colour = NAME
    )
  ) +
  geom_line() + 
  labs(y = "One-way commute VMT per\ncar-dependent worker") + 
  scale_color_discrete(name = "County")
One-way commute miles per car-dependent worker by county, 2011 to 2017. Data from LODES.

Figure 17: One-way commute miles per car-dependent worker by county, 2011 to 2017. Data from LODES.

\(~\)

One of the possible contributions to an increasing VMT/job isn’t necessarily further away jobs but less carpooling to get to those jobs. The following figure shows that the percent of car-dependent SJC workers who carpool has declined from 2015 to 2017, similar to Stanislaus and Sacramento County.

# county_modeshare <- NULL
# 
# for(year in 2011:2017){
#   for(county in county_neighbors$COUNTYFP){
#     
#     print(paste0(year,"-",county))
#     
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/acs1_vars_",year,".Rdata"))
#     
#     travel_time_mode <-
#     getCensus(
#       name = "acs/acs1",
#       vintage = year,
#       region = paste0("county:",county),
#       regionin = "state:06",
#       vars = "group(B08134)"
#     ) %>%
#     select_if(!names(.) %in% c("GEO_ID","state","NAME")) %>%
#     dplyr::select(-c(contains("EA"),contains("MA"),contains("M"))) %>%
#     gather(
#       key = "variable",
#       value = "estimate",
#       -county
#     ) %>%
#     mutate(
#       label = acs_vars$label[match(variable,acs_vars$name)],
#       time = # This extracts only the time information from our label.
#         substr(
#           label,
#           lapply(
#             label,
#             function(x){
#               ifelse(
#                 length(unlist(gregexpr('!!',x)))<3,
#                 NA,
#                 max(unlist(gregexpr('!!',x)))+2
#               )
#             }
#           ),
#           nchar(label)
#         ),
#       mode = # This extracts only the mode information from our label. It doesn't fully deal with double-counting, so some are further removed later on in a filter.
#         substr(
#           label,
#           lapply(
#             label,
#             function(x){
#               ifelse(
#                 length(unlist(gregexpr('!!',x)))<3,
#                 NA,
#                 unlist(gregexpr('!!',x))[length(unlist(gregexpr('!!',x)))-1]+2
#               )
#             }
#           ),
#           lapply(
#             label,
#             function(x){
#               max(unlist(gregexpr('!!',x)))-1
#             }
#           )
#         )
#     ) %>%
#     filter(!is.na(time)) %>% # This removes the grand total rows that are usually at the top of the ACS data.
#     filter(!mode %in% c("Car, truck, or van","Car truck or van","Carpooled","Public transportation (excluding taxicab)")) %>% # This removes double-counted subtotals.
#     dplyr::select(-variable,-label)
#     
#     travel_time_mode_summary <-
#       travel_time_mode %>%
#       summarize(
#         jobs = sum(estimate),
#         jobs_drovealone = sum(estimate[which(mode == "Drove alone")]),
#         jobs_carpool2 = sum(estimate[which(mode == "In 2-person carpool")]),
#         jobs_carpool3 = sum(estimate[which(mode == "In 3-or-more-person carpool")]),
#       ) %>% 
#       mutate(
#         vehicles_drovealone = jobs_drovealone,
#         vehicles_carpool2 = jobs_carpool2/2,
#         vehicles_carpool3 = jobs_carpool3/3,
#         jobs_car = jobs_drovealone+jobs_carpool2+jobs_carpool3,
#         jobs_carpool = jobs_carpool2+jobs_carpool3,
#         vehicles = vehicles_drovealone,vehicles_carpool2,vehicles_carpool3,
#         perc_jobs_car = jobs_car/jobs,
#         perc_jobs_carpool = jobs_carpool/jobs,
#         perc_vehicle = vehicles/jobs
#       )
#     
#     county_modeshare <-
#       county_modeshare %>% 
#       rbind(
#         data.frame(
#           year = year,
#           county = county,
#           jobs = travel_time_mode_summary$jobs,
#           perc_jobs_car = travel_time_mode_summary$perc_jobs_car,
#           perc_jobs_carpool = travel_time_mode_summary$perc_jobs_carpool,
#           perc_vehicle = travel_time_mode_summary$perc_vehicle
#         )
#       )
#   }
# }
# 
# save(county_modeshare, file = "C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_modeshare.Rdata")
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_modeshare.Rdata")
# 
# county_modeshare %>% 
#   filter(county %in% c("077","001","013","067","099")) %>% 
#   filter(jobs != 0) %>% 
#   left_join(county_neighbors, by = c("county" = "COUNTYFP")) %>% 
#   ggplot(
#     aes(
#       x = year,
#       y = perc_jobs_carpool/perc_jobs_car*100,
#       colour = NAME
#     )
#   ) +
#   geom_line() +
#   labs(x = "Year", y = "% Car-dependent workers who carpool") + 
#   scale_color_discrete(name = "County")

county_commute_vmt_tractmode %>% 
  left_join(county_neighbors, by = c("County" = "COUNTYFP")) %>% 
  ggplot(
    aes(
      x = Year,
      y = jobs_carpool/jobs_car,
      colour = NAME
    )
  ) +
  geom_line() + 
  labs(y = "% Car-dependent workers who carpool") + 
  scale_color_discrete(name = "County")
Percent of car-dependent commuters traveling by carpool, by county, 2011 to 2017. Data from LODES and ACS.

Figure 18: Percent of car-dependent commuters traveling by carpool, by county, 2011 to 2017. Data from LODES and ACS.

\(~\)

2.5.2 City-level Analysis

The following graph shows the distribution of workplaces by distance from home for Stockton residents.

# for(year in 2011:2017){
#   
#   print(year)
#   
#   ca_lodes <-
#     grab_lodes(
#       state = "ca",
#       year = year,
#       lodes_type = "od",
#       job_type = "JT01", #Primary Jobs
#       segment = "S000",
#       state_part = "main",
#       agg_geo = "bg"
#     )
#   
#   stockton_lodes_h <-
#     ca_lodes[which(ca_lodes$h_bg %in% stockton_bgs_full$GEOID),]
#   
#   stockton_lodes_h_origin_centroids <-
#     st_centroid(ca_bgs[which(ca_bgs$GEOID %in% stockton_lodes_h$h_bg),])
#   
#   stockton_lodes_h_dest_centroids <-
#     st_centroid(ca_bgs[which(ca_bgs$GEOID %in% stockton_lodes_h$w_bg),])
#   
#   route <-
#     1:nrow(stockton_lodes_h) %>%
#     map_dfr(function(row){
#       print(row)
#       route <- osrmRoute(
#         src = stockton_lodes_h_origin_centroids[which(stockton_lodes_h_origin_centroids$GEOID %in% stockton_lodes_h[row,"h_bg"]),],
#         dst = stockton_lodes_h_dest_centroids[which(stockton_lodes_h_dest_centroids$GEOID %in% stockton_lodes_h[row,"w_bg"]),],
#         overview = FALSE
#       ) %>%
#         as.list() %>%
#         as.data.frame()
#       if(is_empty(route)){
#         return(
#           data.frame(
#             duration = NA,
#             distance = NA
#           )
#         )
#       } else {return(route)}
#     })
#   
#   stockton_lodes_h_route <-
#     stockton_lodes_h %>%
#     cbind(route)
#   
#   stockton_lodes_h_filter <-
#     stockton_lodes_h_route %>%
#     filter(duration < 180)
#   
#   save(stockton_lodes_h_route,stockton_lodes_h_filter, file = paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_lodes_",year,".Rdata"))
# }

load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_lodes_2017_tract.Rdata")

ggplot(
  stockton_lodes_h_filter, 
  aes(
    x = as.numeric(distance)/1.60934, 
    weight = S000
  )
) +
  geom_histogram(binwidth = 5) +
  labs(title = "Workplace Commute Distance to Work for Stockton Employed Residents", x = "Commute Distance to Work, Miles", y = "Number of Residents")
Distribution of workplaces by distance from home for Stockton employed residents. Trips greater than 3 hours are removed. Data from LODES, 2017.

Figure 19: Distribution of workplaces by distance from home for Stockton employed residents. Trips greater than 3 hours are removed. Data from LODES, 2017.

\(~\)

The map below demonstrates the extent of geographic spread of Stockton residents across the greater Northern California region, potentially on a day-to-day basis. Most of the tracts shown have only a handful of estimated Stockton workers, but interesting concentrations of workers can be seen by zooming in and hovering over areas like the East Bay.

stockton_jobs_map <-
  stockton_lodes_h_filter %>% 
  group_by(w_tract) %>% 
  summarize(
    `Jobs held by Stockton residents` = sum(S000, na.rm=T)
  ) %>% 
  left_join(ca_tracts, by = c("w_tract" = "GEOID")) %>%
  st_as_sf()

map = mapview(stockton_jobs_map, zcol = "Jobs held by Stockton residents", col.regions = colorRampPalette(c("snow", "black", "grey")), map.types = c("OpenStreetMap"), legend = TRUE, layer.name = 'Jobs held by</br>Stockton residents')

map@map

Figure 20: Tracts where Stockton residents work - number of jobs. Data from LODES, 2017.

\(~\)

The previous map can be considered in combination with the map below, which essentially shows the average distance in miles traveled one-way by Stockton workers to those tracts. Evidently, workers traveling to further-away tracts are responsible for a disproportionate share of the transportation emissions compared to other Stockton workers.

stockton_commute_avg <-
  stockton_lodes_h_filter %>% 
  group_by(w_tract) %>% 
  summarize(
    `Average Commute Time, Hours` = mean(duration, na.rm=T)
  ) %>% 
  left_join(ca_tracts, by = c("w_tract" = "GEOID")) %>%
  st_as_sf()

map = mapview(stockton_commute_avg, zcol = "Average Commute Time, Hours", map.types = c("OpenStreetMap"), legend = TRUE, layer.name = 'Average Commute</br>Time, Hours')

map@map

Figure 21: Tracts where Stockton residents work - average commute time, in hours. Data from LODES, 2017.

\(~\)

Similar to the county-level analysis, we calculated the change in VMT/job over time, which might give more important relative insight given the many assumptions that go into the specific quantity of VMTs in any given year.

load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_tractmode.Rdata")

stockton_commute_vmt_tractmode_table <-
  stockton_commute_vmt_tractmode %>% 
  transmute(
    Year = Year,
    `Commute One-Way VMT` = prettyNum(round(vmt,-3), big.mark=","),
    `Workers` = prettyNum(round(jobs,-3), big.mark=","),
    `One-Way VMT/Workers` = round(vmt/jobs_car,1),
    `% Car-Dependent Workers who Carpool` = paste0(round(jobs_carpool/jobs_car*100,1),"%")
  )

kable(
  stockton_commute_vmt_tractmode_table,
  booktabs = TRUE,
  caption = 'Stockton commute one-way VMT from 2011-2017. Data from LODES and ACS 1-yr.'
) %>%
  kable_styling() %>%
  scroll_box(width = "100%")
Table 17: Stockton commute one-way VMT from 2011-2017. Data from LODES and ACS 1-yr.
Year Commute One-Way VMT Workers One-Way VMT/Workers % Car-Dependent Workers who Carpool
2011 1,837,000 98,000 20.3 18.2%
2012 1,932,000 99,000 20.9 18.3%
2013 1,979,000 102,000 20.9 19%
2014 2,228,000 108,000 22.0 20.1%
2015 2,226,000 114,000 21.0 20.2%
2016 2,389,000 119,000 21.5 19.8%
2017 2,635,000 123,000 23.0 18.8%

\(~\)

The average one-way commute trip for the Stockton worker appears to have increased by 13% over the last 6 years. This could be explained by existing residents changing to jobs that are further and further away from home, where better employment opportunities can be found, and by new residents moving to Stockton to find affordable housing but retaining their faraway jobs.

The following figure compares Stockton and SJC VMT/job. The trends are similar, though Stockton residents commute less distance than SJC residents overall.

# stockton_commute_vmt_bgmode <- NULL
# 
# for(year in 2011:2017){
#     print(year)
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_lodes_",year,".Rdata"))
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/acs5_vars_",year,".Rdata"))
# 
#     travel_time_mode <-
#       getCensus(
#         name = "acs/acs5",
#         vintage = ifelse(
#           year < 2013,
#           2013,
#           year
#         ),
#         region = "block group:*",
#         regionin = "state:06+county:077",
#         vars = "group(B08134)"
#       ) %>%
#       mutate(bg = paste0(state,county,tract,block_group)) %>%
#       select_if(!names(.) %in% c("GEO_ID","state","county","tract","block_group","NAME")) %>%
#       dplyr::select(-c(contains("EA"),contains("MA"),contains("M"))) %>%
#       gather(
#         key = "variable",
#         value = "estimate",
#         -bg
#       ) %>%
#       mutate(
#         label = acs_vars$label[match(variable,acs_vars$name)],
#         time = # This extracts only the time information from our label.
#           substr(
#             label,
#             lapply(
#               label,
#               function(x){
#                 ifelse(
#                   length(unlist(gregexpr('!!',x)))<3,
#                   NA,
#                   max(unlist(gregexpr('!!',x)))+2
#                 )
#               }
#             ),
#             nchar(label)
#           ),
#         mode = # This extracts only the mode information from our label. It doesn't fully deal with double-counting, so some are further removed later on in a filter.
#           substr(
#             label,
#             lapply(
#               label,
#               function(x){
#                 ifelse(
#                   length(unlist(gregexpr('!!',x)))<3,
#                   NA,
#                   unlist(gregexpr('!!',x))[length(unlist(gregexpr('!!',x)))-1]+2
#                 )
#               }
#             ),
#             lapply(
#               label,
#               function(x){
#                 max(unlist(gregexpr('!!',x)))-1
#               }
#             )
#           )
#       ) %>%
#       filter(!is.na(time)) %>% # This removes the grand total rows that are usually at the top of the ACS data.
#       filter(!mode %in% c("Car, truck, or van","Car truck or van","Carpooled","Public transportation (excluding taxicab)")) %>% # This removes double-counted subtotals.
#       dplyr::select(-variable,-label)
# 
#     travel_time_mode_summary <-
#       travel_time_mode %>%
#       group_by(bg,time) %>%
#       summarize(
#         jobs = sum(estimate),
#         jobs_drovealone = sum(estimate[which(mode == "Drove alone")]),
#         jobs_carpool2 = sum(estimate[which(mode == "In 2-person carpool")]),
#         jobs_carpool3 = sum(estimate[which(mode == "In 3-or-more-person carpool")]),
#       ) %>%
#       mutate(
#         vehicles_drovealone = jobs_drovealone,
#         vehicles_carpool2 = jobs_carpool2/2,
#         vehicles_carpool3 = jobs_carpool3/3,
#         jobs_car = jobs_drovealone+jobs_carpool2+jobs_carpool3,
#         jobs_carpool = jobs_carpool2+jobs_carpool3,
#         vehicles = vehicles_drovealone,vehicles_carpool2,vehicles_carpool3,
#         perc_jobs_car = jobs_car/jobs,
#         perc_jobs_carpool = jobs_carpool/jobs,
#         perc_vehicle = vehicles/jobs
#       )
# 
#   stockton_lodes_h_mode <-
#     stockton_lodes_h_filter %>%
#     transmute(
#       residence = h_bg,
#       jobs = S000,
#       duration = duration,
#       person_miles = S000*as.numeric(distance)/1.60934,
#       person_hours = S000*as.numeric(duration)/60
#     ) %>%
#     mutate(
#       tier =
#         case_when(
#           duration < 10 ~ "Less than 10 minutes",
#           duration < 15 ~ "10 to 14 minutes",
#           duration < 20 ~ "15 to 19 minutes",
#           duration < 25 ~ "20 to 24 minutes",
#           duration < 30 ~ "25 to 29 minutes",
#           duration < 35 ~ "30 to 34 minutes",
#           duration < 45 ~ "35 to 44 minutes",
#           duration < 60 ~ "45 to 59 minutes",
#           TRUE ~ "60 or more minutes"
#         )
#     ) %>%
#     left_join(
#       travel_time_mode_summary %>%
#         dplyr::select(
#           bg,
#           time,
#           perc_jobs_car,
#           perc_jobs_carpool,
#           perc_vehicle
#         ),
#       by = c("residence" = "bg", "tier" = "time")
#     ) %>%
#     mutate(
#       jobs_car = jobs*perc_jobs_car,
#       jobs_carpool = jobs*perc_jobs_carpool,
#       vehicles = jobs*perc_vehicle,
#       vmt = person_miles*perc_vehicle
#     )
# 
#     stockton_commute_vmt_bgmode <-
#       rbind(stockton_commute_vmt_bgmode,
#         data.frame(
#           Year = year,
#           person_miles = sum(stockton_lodes_h_mode$person_miles, na.rm=T),
#           jobs = sum(stockton_lodes_h_mode$jobs, na.rm=T),
#           jobs_car = sum(stockton_lodes_h_mode$jobs_car, na.rm=T),
#           jobs_carpool = sum(stockton_lodes_h_mode$jobs_carpool, na.rm=T),
#           vehicles = sum(stockton_lodes_h_mode$vehicles, na.rm = T),
#           vmt = sum(stockton_lodes_h_mode$vmt, na.rm=T)
#         )
#       )
# 
#     save(stockton_commute_vmt_bgmode,file = "C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_bgmode.Rdata")
# }
#
# stockton_commute_vmt_tractmode <- NULL
# 
# for(year in 2011:2017){
#     print(year)
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_lodes_",year,"_tract.Rdata"))
# 
#     load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/acs5_vars_",year,".Rdata"))
# 
#     travel_time_mode <-
#       getCensus(
#         name = "acs/acs5",
#         vintage = year,
#         region = "tract:*",
#         regionin = "state:06+county:077",
#         vars = "group(B08134)"
#       ) %>%
#       mutate(tract = paste0(state,county,tract)) %>%
#       select_if(!names(.) %in% c("GEO_ID","state","county","NAME")) %>%
#       dplyr::select(-c(contains("EA"),contains("MA"),contains("M"))) %>%
#       gather(
#         key = "variable",
#         value = "estimate",
#         -tract
#       ) %>%
#       mutate(
#         label = acs_vars$label[match(variable,acs_vars$name)],
#         time = # This extracts only the time information from our label.
#           substr(
#             label,
#             lapply(
#               label,
#               function(x){
#                 ifelse(
#                   length(unlist(gregexpr('!!',x)))<3,
#                   NA,
#                   max(unlist(gregexpr('!!',x)))+2
#                 )
#               }
#             ),
#             nchar(label)
#           ),
#         mode = # This extracts only the mode information from our label. It doesn't fully deal with double-counting, so some are further removed later on in a filter.
#           substr(
#             label,
#             lapply(
#               label,
#               function(x){
#                 ifelse(
#                   length(unlist(gregexpr('!!',x)))<3,
#                   NA,
#                   unlist(gregexpr('!!',x))[length(unlist(gregexpr('!!',x)))-1]+2
#                 )
#               }
#             ),
#             lapply(
#               label,
#               function(x){
#                 max(unlist(gregexpr('!!',x)))-1
#               }
#             )
#           )
#       ) %>%
#       filter(!is.na(time)) %>% # This removes the grand total rows that are usually at the top of the ACS data.
#       filter(!mode %in% c("Car, truck, or van","Car truck or van","Carpooled","Public transportation (excluding taxicab)")) %>% # This removes double-counted subtotals.
#       dplyr::select(-variable,-label)
# 
#     travel_time_mode_summary <-
#       travel_time_mode %>%
#       group_by(tract,time) %>%
#       summarize(
#         jobs = sum(estimate),
#         jobs_drovealone = sum(estimate[which(mode == "Drove alone")]),
#         jobs_carpool2 = sum(estimate[which(mode == "In 2-person carpool")]),
#         jobs_carpool3 = sum(estimate[which(mode == "In 3-or-more-person carpool")]),
#       ) %>%
#       mutate(
#         vehicles_drovealone = jobs_drovealone,
#         vehicles_carpool2 = jobs_carpool2/2,
#         vehicles_carpool3 = jobs_carpool3/3,
#         jobs_car = jobs_drovealone+jobs_carpool2+jobs_carpool3,
#         jobs_carpool = jobs_carpool2+jobs_carpool3,
#         vehicles = vehicles_drovealone,vehicles_carpool2,vehicles_carpool3,
#         perc_jobs_car = jobs_car/jobs,
#         perc_jobs_carpool = jobs_carpool/jobs,
#         perc_vehicle = vehicles/jobs
#       )
# 
#   stockton_lodes_h_mode <-
#     stockton_lodes_h_filter %>%
#     transmute(
#       residence = h_tract,
#       jobs = S000,
#       duration = duration,
#       person_miles = S000*as.numeric(distance)/1.60934,
#       person_hours = S000*as.numeric(duration)/60
#     ) %>%
#     mutate(
#       tier =
#         case_when(
#           duration < 10 ~ "Less than 10 minutes",
#           duration < 15 ~ "10 to 14 minutes",
#           duration < 20 ~ "15 to 19 minutes",
#           duration < 25 ~ "20 to 24 minutes",
#           duration < 30 ~ "25 to 29 minutes",
#           duration < 35 ~ "30 to 34 minutes",
#           duration < 45 ~ "35 to 44 minutes",
#           duration < 60 ~ "45 to 59 minutes",
#           TRUE ~ "60 or more minutes"
#         )
#     ) %>%
#     left_join(
#       travel_time_mode_summary %>%
#         dplyr::select(
#           tract,
#           time,
#           perc_jobs_car,
#           perc_jobs_carpool,
#           perc_vehicle
#         ),
#       by = c("residence" = "tract", "tier" = "time")
#     ) %>%
#     mutate(
#       jobs_car = jobs*perc_jobs_car,
#       jobs_carpool = jobs*perc_jobs_carpool,
#       vehicles = jobs*perc_vehicle,
#       vmt = person_miles*perc_vehicle
#     )
# 
#     stockton_commute_vmt_tractmode <-
#       rbind(stockton_commute_vmt_tractmode,
#         data.frame(
#           Year = year,
#           person_miles = sum(stockton_lodes_h_mode$person_miles, na.rm=T),
#           jobs = sum(stockton_lodes_h_mode$jobs, na.rm=T),
#           jobs_car = sum(stockton_lodes_h_mode$jobs_car, na.rm=T),
#           jobs_carpool = sum(stockton_lodes_h_mode$jobs_carpool, na.rm=T),
#           vehicles = sum(stockton_lodes_h_mode$vehicles, na.rm = T),
#           vmt = sum(stockton_lodes_h_mode$vmt, na.rm=T)
#         )
#       )
# 
#     save(stockton_commute_vmt_tractmode,file = "C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_tractmode.Rdata")
# }
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_tractmode.Rdata")
# 
# stockton_commute_vmt_tractmode %>%
#   ggplot(
#     aes(
#       x = Year,
#       y = vmt/jobs
#     )
#   ) +
#   geom_line()
# 
# stockton_commute_vmt_countymode <- NULL
# 
# for(year in 2011:2017){
# 
#   print(year)
# 
#   load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_lodes_",year,"_tract.Rdata"))
# 
#   load(paste0("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/acs1_vars_",year,".Rdata"))
# 
#   travel_time_mode <-
#     getCensus(
#       name = "acs/acs1",
#       vintage = year,
#       region = "county:077",
#       regionin = "state:06",
#       vars = "group(B08134)"
#     ) %>%
#     select_if(!names(.) %in% c("GEO_ID","state","NAME")) %>%
#     dplyr::select(-c(contains("EA"),contains("MA"),contains("M"))) %>%
#     gather(
#       key = "variable",
#       value = "estimate",
#       -county
#     ) %>%
#     mutate(
#       label = acs_vars$label[match(variable,acs_vars$name)],
#       time = # This extracts only the time information from our label.
#         substr(
#           label,
#           lapply(
#             label,
#             function(x){
#               ifelse(
#                 length(unlist(gregexpr('!!',x)))<3,
#                 NA,
#                 max(unlist(gregexpr('!!',x)))+2
#               )
#             }
#           ),
#           nchar(label)
#         ),
#       mode = # This extracts only the mode information from our label. It doesn't fully deal with double-counting, so some are further removed later on in a filter.
#         substr(
#           label,
#           lapply(
#             label,
#             function(x){
#               ifelse(
#                 length(unlist(gregexpr('!!',x)))<3,
#                 NA,
#                 unlist(gregexpr('!!',x))[length(unlist(gregexpr('!!',x)))-1]+2
#               )
#             }
#           ),
#           lapply(
#             label,
#             function(x){
#               max(unlist(gregexpr('!!',x)))-1
#             }
#           )
#         )
#     ) %>%
#     filter(!is.na(time)) %>% # This removes the grand total rows that are usually at the top of the ACS data.
#     filter(!mode %in% c("Car, truck, or van","Car truck or van","Carpooled","Public transportation (excluding taxicab)")) %>% # This removes double-counted subtotals.
#     dplyr::select(-variable,-label)
# 
#     travel_time_mode_summary <-
#       travel_time_mode %>%
#       group_by(time) %>%
#       summarize(
#         jobs = sum(estimate),
#         jobs_drovealone = sum(estimate[which(mode == "Drove alone")]),
#         jobs_carpool2 = sum(estimate[which(mode == "In 2-person carpool")]),
#         jobs_carpool3 = sum(estimate[which(mode == "In 3-or-more-person carpool")]),
#       ) %>%
#       mutate(
#         vehicles_drovealone = jobs_drovealone,
#         vehicles_carpool2 = jobs_carpool2/2,
#         vehicles_carpool3 = jobs_carpool3/3,
#         jobs_car = jobs_drovealone+jobs_carpool2+jobs_carpool3,
#         jobs_carpool = jobs_carpool2+jobs_carpool3,
#         vehicles = vehicles_drovealone,vehicles_carpool2,vehicles_carpool3,
#         perc_jobs_car = jobs_car/jobs,
#         perc_jobs_carpool = jobs_carpool/jobs,
#         perc_vehicle = vehicles/jobs
#       )
# 
#   stockton_lodes_h_mode <-
#     stockton_lodes_h_filter %>%
#     transmute(
#       jobs = S000,
#       duration = duration,
#       person_miles = S000*as.numeric(distance)/1.60934,
#       person_hours = S000*as.numeric(duration)/60
#     ) %>%
#     mutate(
#       tier =
#         case_when(
#           duration < 10 ~ "Less than 10 minutes",
#           duration < 15 ~ "10 to 14 minutes",
#           duration < 20 ~ "15 to 19 minutes",
#           duration < 25 ~ "20 to 24 minutes",
#           duration < 30 ~ "25 to 29 minutes",
#           duration < 35 ~ "30 to 34 minutes",
#           duration < 45 ~ "35 to 44 minutes",
#           duration < 60 ~ "45 to 59 minutes",
#           TRUE ~ "60 or more minutes"
#         )
#     ) %>%
#     left_join(
#       travel_time_mode_summary %>%
#         dplyr::select(
#           time,
#           perc_jobs_car,
#           perc_jobs_carpool,
#           perc_vehicle
#         ),
#       by = c("tier" = "time")
#     ) %>%
#     mutate(
#       jobs_car = jobs*perc_jobs_car,
#       jobs_carpool = jobs*perc_jobs_carpool,
#       vehicles = jobs*perc_vehicle,
#       vmt = person_miles*perc_vehicle
#     )
# 
#     stockton_commute_vmt_countymode <-
#       rbind(stockton_commute_vmt_countymode,
#         data.frame(
#           Year = year,
#           person_miles = sum(stockton_lodes_h_mode$person_miles, na.rm=T),
#           jobs = sum(stockton_lodes_h_mode$jobs, na.rm=T),
#           jobs_car = sum(stockton_lodes_h_mode$jobs_car, na.rm=T),
#           jobs_carpool = sum(stockton_lodes_h_mode$jobs_carpool, na.rm=T),
#           vehicles = sum(stockton_lodes_h_mode$vehicles, na.rm = T),
#           vmt = sum(stockton_lodes_h_mode$vmt, na.rm=T)
#         )
#       )
# }
# 
# save(stockton_commute_vmt_countymode, file = "C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_countymode.Rdata")
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_countymode.Rdata")
# 
# stockton_commute_vmt_countymode %>%
#   ggplot(
#     aes(
#       x = Year,
#       y = vmt/jobs
#     )
#   ) +
#   geom_line()
# 
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_bgmode.Rdata")
# 
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_tractmode.Rdata")
# 
# load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_countymode.Rdata")
# 
# stockton_commute_vmt_compare <-
#   rbind(
#     stockton_commute_vmt_bgmode %>% 
#       mutate(mode = "block group"),
#     stockton_commute_vmt_tractmode %>% 
#       mutate(mode = "tract"),
#     stockton_commute_vmt_countymode %>% 
#       mutate(mode = "county")
#   )
# 
# stockton_commute_vmt_compare %>%
#   ggplot(
#     aes(
#       x = Year,
#       y = vmt/jobs,
#       linetype = mode
#     )
#   ) +
#   geom_line()

county_commute_vmt_tractmode %>% 
  filter(County == "077") %>% 
  ggplot(
    aes(
      x = Year,
      y = vmt/jobs_car,
      linetype = "SJC"
    )
  ) +
  geom_line() + 
  geom_line(
    data = stockton_commute_vmt_tractmode,
    aes(
      x = Year,
      y = vmt/jobs_car,
      linetype = "Stockton"
    )
  ) +
  labs(y = "One-way commute miles\nper car-dependent worker", linetype = "Group")
One-way commute miles per car-dependent worker in Stockton vs. SJC, 2011 to 2017. Data from LODES.

Figure 22: One-way commute miles per car-dependent worker in Stockton vs. SJC, 2011 to 2017. Data from LODES.

\(~\)

The following figure compares the percent of car-dependent workers who carpool in Stockton and SJC. Stockton workers overall are more likely to carpool than SJC workers overall, but the trends are again similar between Stockton and SJC, with a decline in carpooling in the last two years of data.

load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_commute_vmt_tractmode.Rdata")

load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/county_commute_vmt_tractmode.Rdata")

stockton_county_commute_vmt_compare <-
  rbind(
    county_commute_vmt_tractmode %>% 
      filter(County == "077") %>% 
      dplyr::select(-County) %>% 
      mutate(type = "SJC"), 
    stockton_commute_vmt_tractmode %>% 
      mutate(type = "Stockton")
  )

stockton_county_commute_vmt_compare %>%
  ggplot(
    aes(
      x = Year,
      y = jobs_carpool/jobs_car*100,
      linetype = type
    )
  ) +
  geom_line() +
  labs(y = "% Car-dependent workers who carpool", linetype = "Group")
Percent of car-dependent commuters traveling by carpool, Stockton vs. SJC, 2011 to 2017. Data from LODES and ACS.

Figure 23: Percent of car-dependent commuters traveling by carpool, Stockton vs. SJC, 2011 to 2017. Data from LODES and ACS.

\(~\)

We estimated the total and average GHG emissions for Stockton workers commuting to each of the top 15 counties (filtering out counties further than 3 hours away). We made the following assumptions:

  • For daily VMT, each vehicle was assumed to take 2 one-way trips.
  • To convert daily VMT to annual VMT, the same annualization factor was used as ICLEI: 369.39. The factor appears to account for both expected reductions in VMT because of sick days, as well as expected increases in VMT because of chained trips.
  • All vehicles were assumed to be passenger vehicles with an average mpg of 29.2 in 2017 and CO2e emissions of 303 g/mi (0.000334 tCO2e/mi), using data from the EPA Automotive Trends Report. Disregarding SUVs and trucks has the effect of underestimating emissions.

The following table is sorted by total annual GHG from transportation.

load("C:/Users/derek/Google Drive/City Systems/Stockton Green Economy/LODES/stockton_lodes_2017.Rdata")

stockton_lodes_h_counties_ghg <- 
  stockton_lodes_h_mode %>%
  st_set_geometry(NULL) %>% 
  mutate(
    Workplace = ifelse(
      workplace %in% stockton_bgs_full$GEOID,
      "75000",
      substr(workplace,3,5)
    )
  ) %>%
  group_by(Workplace) %>%
  summarise_at(
    vars(jobs,person_miles,person_hours,vehicles,vmt),
    sum, na.rm=T
  ) %>% 
  mutate(
    perc_jobs = jobs/sum(jobs),
    annual_vmt = vmt*2*369.39,
    annual_ghg = annual_vmt*0.000334,
    perc_ghg = annual_ghg/sum(annual_ghg),
    avg_ghg = annual_ghg/jobs
  ) %>% 
  left_join(ca_counties_and_stockton %>% 
  dplyr::select(COUNTYFP, NAME), by = c("Workplace" = "COUNTYFP")) %>% 
  dplyr::select(-Workplace) %>%
  dplyr::select(NAME,everything()) %>% 
  st_as_sf() %>% 
  arrange(desc(annual_ghg))
  
stockton_lodes_h_counties_ghg_table <- 
  stockton_lodes_h_counties_ghg[1:15,] %>%
  transmute(
    `Workplace (where Stockton residents work)` = NAME,
    `Jobs (held by Stockton residents)` = prettyNum(round(jobs,-2),big.mark=","),
    `Percent Jobs` = paste0(round(perc_jobs*100),"%"),
    `VMT (millions)` = prettyNum(round(annual_vmt/1000000),big.mark=","),
    `Total Annual GHG (tCO2e)` = prettyNum(round(annual_ghg,-3),big.mark=","),
    `Percent Annual GHG` = paste0(round(perc_ghg*100),"%"),
    `Average Annual GHG/worker (tCO2e)` = round(avg_ghg,1)
  )

kable(
  stockton_lodes_h_counties_ghg_table %>% st_set_geometry(NULL),
  booktabs = TRUE,
  caption = 'Top 15 workplaces where Stockton residents work, GHG emissions from transportation. Includes Stockton as a workplace destination separate from the rest of SJC. All other listed workplaces are counties. Los Angeles, San Bernardino, Orange, and San Diego Counties were removed. Data from LODES, 2017.'
) %>%
  kable_styling() %>%
  scroll_box(width = "100%")
Table 18: Top 15 workplaces where Stockton residents work, GHG emissions from transportation. Includes Stockton as a workplace destination separate from the rest of SJC. All other listed workplaces are counties. Los Angeles, San Bernardino, Orange, and San Diego Counties were removed. Data from LODES, 2017.
Workplace (where Stockton residents work) Jobs (held by Stockton residents) Percent Jobs VMT (millions) Total Annual GHG (tCO2e) Percent Annual GHG Average Annual GHG/worker (tCO2e)
Alameda 9,100 8% 304 102,000 15% 11.2
Santa Clara 5,700 5% 244 81,000 12% 14.2
Sacramento 8,900 8% 209 70,000 10% 7.8
Stockton 51,500 44% 191 64,000 9% 1.2
San Joaquin (not including Stockton) 17,700 15% 177 59,000 9% 3.3
San Francisco 3,100 3% 143 48,000 7% 15.5
Contra Costa 4,400 4% 123 41,000 6% 9.4
San Mateo 2,300 2% 105 35,000 5% 15.3
Fresno 1,300 1% 92 31,000 5% 23.2
Stanislaus 4,500 4% 76 25,000 4% 5.6
Solano 1,600 1% 53 18,000 3% 10.7
Placer 1,200 1% 42 14,000 2% 12.1
Sonoma 700 1% 40 13,000 2% 18.5
Monterey 500 0% 36 12,000 2% 24.6
Yolo 900 1% 27 9,000 1% 10.4

\(~\)

According to the table above, 59% percent of Stockton residents work in San Joaquin County and collectively contribute 18% of all commute GHGs. The next 14 counties comprise another 38% of Stockton residents, but they collectively contribute 76% of all commute GHGs. This significant difference comes down to difference in average commute distance to each county, which is directly related to average GHG emissions per worker. In a later section, we will explore opportunities to stimulate local job creation so that some of these long-distance commutes can be converted into local commutes, reducing both the toll on the environment and the tolls on health and well-being for Stockton residents.

map = mapview(stockton_lodes_h_counties_ghg[1:15,], zcol = "annual_ghg", map.types = c("OpenStreetMap"), legend = TRUE, layer.name = 'Total Annual</br>GHG (tCO2e)')

map@map

Figure 24: Top 15 Counties where Stockton residents work - annual GHG emissions from driving. Data from LODES, 2017.

\(~\)